/*
 moxl - Modular XML Library
 Copyright (c) 2007, Malcolm Sparks

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package moxl.serialization;

import java.io.PrintWriter;

import moxl.dom.MoxlAttribute;
import moxl.dom.MoxlCharacterData;
import moxl.dom.MoxlComment;
import moxl.dom.DefaultXmlNamespace;
import moxl.dom.MoxlElement;
import moxl.dom.MoxlNode;
import moxl.dom.MoxlNodeVisitor;

public class SerializingVisitor implements MoxlNodeVisitor
{
	private final PrintWriter out;
	private int indent;

	public SerializingVisitor(PrintWriter out)
	{
		this.out = out;
	}

	public void startNode(MoxlNode node)
	{
		if (node instanceof MoxlElement)
		{
			startElement((MoxlElement) node);
		}
		else if (node instanceof MoxlCharacterData)
		{
			startCharacterData((MoxlCharacterData) node);
		}
		else if (node instanceof MoxlComment)
		{
			startComment((MoxlComment) node);
		}
		else
		{
			throw new UnsupportedOperationException("TODO: Implement for " + node.getClass().getName());
		}
	}

	private void startCharacterData(MoxlCharacterData cdata)
	{
		if (cdata.getData().trim().length() > 0)
		{
			out.print(cdata.getData().trim());
		}
	}

	private void startComment(MoxlComment comment)
	{
		out.print("<!-- ");
		out.print(comment.getBodyText());
		out.println(" -->");
	}

	public void endNode(MoxlNode node)
	{
		if (node instanceof MoxlElement)
		{
			endElement((MoxlElement) node);
		}
		else if (node instanceof MoxlCharacterData)
		{
			return;
		}
		else if (node instanceof MoxlComment)
		{
			return;
		}
		else
		{
			throw new UnsupportedOperationException("TODO: Implement for " + node.getClass().getName());
		}
	}

	private void startElement(MoxlElement element)
	{
		for (int i = 0; i < indent; i++)
		{
			tab(out);
		}
		out.print("<");
		out.print(element.getName().getLocalPart());
		printAttributes(element);
		if (element.isEmpty())
		{
			out.print("/");
		}
		else if (!element.containsCharacterDataExcludingWhitespace())
		{
			indent++;
		}
		out.print(">");
		if (element.isEmpty() || !element.containsCharacterDataExcludingWhitespace())
		{
			out.println();
		}
	}

	private void printAttributes(MoxlElement element)
	{
		for (MoxlAttribute attr : element.getAttributeMap())
		{
			out.print(" ");
			if (attr.getQName().getNamespace().equals(DefaultXmlNamespace.getInstance()))
			{
				out.print(attr.getQName().getLocalPart());
			}
			else
			{
				throw new UnsupportedOperationException(
				        "Cannot yet serialize attributes that have a non-default namespace, need to lookup prefix of namespace in scope first");
			}
			out.print("=");
			out.print('\"');
			out.print(attr.getValue());
			out.print('\"');
		}
	}

	private void tab(PrintWriter out)
	{
		out.print("\t");
	}

	private void endElement(MoxlElement element)
	{
		if (!element.isEmpty())
		{
			if (element.containsCharacterDataExcludingWhitespace())
			{
				out.print("</");
				out.print(element.getName().getLocalPart());
				out.println(">");
				return;
			}
			else
			{
				indent--;
			}
		}
		if (!element.isEmpty())
		{
			for (int i = 0; i < indent; i++)
			{
				tab(out);
			}
			out.print("</");
			out.print(element.getName().getLocalPart());
			out.println(">");
		}

	}
}
